package com.duckclouddrive.service.impl;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import com.duckclouddrive.component.RedisComponent;
import com.duckclouddrive.entity.config.AppConfig;
import com.duckclouddrive.entity.constants.Constants;
import com.duckclouddrive.entity.dto.QQInfoDto;
import com.duckclouddrive.entity.dto.SessionWebUserDto;
import com.duckclouddrive.entity.dto.SysSettingsDto;
import com.duckclouddrive.entity.dto.UserSpaceDto;
import com.duckclouddrive.entity.enums.PageSize;
import com.duckclouddrive.entity.enums.UserStatusEnum;
import com.duckclouddrive.entity.po.FileInfo;
import com.duckclouddrive.entity.po.UserInfo;
import com.duckclouddrive.entity.query.FileInfoQuery;
import com.duckclouddrive.entity.query.SimplePage;
import com.duckclouddrive.entity.query.UserInfoQuery;
import com.duckclouddrive.entity.vo.PaginationResultVO;
import com.duckclouddrive.exception.BusinessException;
import com.duckclouddrive.mappers.FileInfoMapper;
import com.duckclouddrive.mappers.UserInfoMapper;
import com.duckclouddrive.service.EmailCodeService;
import com.duckclouddrive.service.FileInfoService;
import com.duckclouddrive.service.UserInfoService;
import com.duckclouddrive.utils.JsonUtils;
import com.duckclouddrive.utils.OKHttpUtils;
import com.duckclouddrive.utils.StringTools;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


/**
 *  业务接口实现
 */
@Service("userInfoService")
public class UserInfoServiceImpl implements UserInfoService {
	private static Logger logger = LoggerFactory.getLogger(OKHttpUtils.class);

	@Resource
	private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

	@Resource
	private FileInfoMapper<FileInfo, FileInfoQuery> fileInfoMapper;

	@Resource
	private FileInfoService fileInfoService;

	@Resource
	private EmailCodeService emailCodeService;

	@Resource
	private RedisComponent redisComponent;

	@Resource
	private AppConfig appConfig;



	/**
	 * 根据条件查询列表
	 */
	@Override
	public List<UserInfo> findListByParam(UserInfoQuery param) {
		return this.userInfoMapper.selectList(param);
	}

	/**
	 * 根据条件查询列表
	 */
	@Override
	public Integer findCountByParam(UserInfoQuery param) {
		return this.userInfoMapper.selectCount(param);
	}

	/**
	 * 分页查询方法
	 */
	@Override
	public PaginationResultVO<UserInfo> findListByPage(UserInfoQuery param) {
		int count = this.findCountByParam(param);
		int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

		SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
		param.setSimplePage(page);
		List<UserInfo> list = this.findListByParam(param);
		PaginationResultVO<UserInfo> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
		return result;
	}

	/**
	 * 新增
	 */
	@Override
	public Integer add(UserInfo bean) {
		return this.userInfoMapper.insert(bean);
	}

	/**
	 * 批量新增
	 */
	@Override
	public Integer addBatch(List<UserInfo> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.userInfoMapper.insertBatch(listBean);
	}

	/**
	 * 批量新增或者修改
	 */
	@Override
	public Integer addOrUpdateBatch(List<UserInfo> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.userInfoMapper.insertOrUpdateBatch(listBean);
	}
	/**
	 * 根据UserId获取对象
	 */
	@Override
	public UserInfo getUserInfoByUserId(String userId) {
		return this.userInfoMapper.selectByUserId(userId);
	}

	/**
	 * 根据UserId修改
	 */
	@Override
	public Integer updateUserInfoByUserId(UserInfo bean, String userId) {
		return this.userInfoMapper.updateByUserId(bean, userId);
	}

	/**
	 * 根据UserId删除
	 */
	@Override
	public Integer deleteUserInfoByUserId(String userId) {
		return this.userInfoMapper.deleteByUserId(userId);
	}
	/**
	 * 根据Email获取对象
	 */
	@Override
	public UserInfo getUserInfoByEmail(String email) {
		return this.userInfoMapper.selectByEmail(email);
	}

	/**
	 * 根据Email修改
	 */
	@Override
	public Integer updateUserInfoByEmail(UserInfo bean, String email) {
		return this.userInfoMapper.updateByEmail(bean, email);
	}

	/**
	 * 根据Email删除
	 */
	@Override
	public Integer deleteUserInfoByEmail(String email) {
		return this.userInfoMapper.deleteByEmail(email);
	}

	/**
	 * 根据NickName获取对象
	 */
	@Override
	public UserInfo getUserInfoByNickName(String nickName) {
		return this.userInfoMapper.selectByNickName(nickName);
	}

	/**
	 * 根据NickName修改
	 */
	@Override
	public Integer updateUserInfoByNickName(UserInfo bean, String nickName) {
		return this.userInfoMapper.updateByNickName(bean, nickName);
	}

	/**
	 * 根据NickName删除
	 */
	@Override
	public Integer deleteUserInfoByNickName(String nickName) {
		return this.userInfoMapper.deleteByNickName(nickName);
	}

	/**
	 * 根据QqOpenId获取对象
	 */
	@Override
	public UserInfo getUserInfoByQqOpenId(String qqOpenId) {
		return this.userInfoMapper.selectByQqOpenId(qqOpenId);
	}

	/**
	 * 根据QqOpenId修改
	 */
	@Override
	public Integer updateUserInfoByQqOpenId(UserInfo bean, String qqOpenId) {
		return this.userInfoMapper.updateByQqOpenId(bean, qqOpenId);
	}

	/**
	 * 根据QqOpenId删除
	 */
	@Override
	public Integer deleteUserInfoByQqOpenId(String qqOpenId) {
		return this.userInfoMapper.deleteByQqOpenId(qqOpenId);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	/**
	 * 注册用户
	 *
	 * @param email 用户邮箱
	 * @param nickName 用户昵称
	 * @param password 用户密码
	 * @param emailCode 邮箱验证码
	 * @throws BusinessException 当邮箱或昵称已存在时抛出业务异常
	 */
	public void register(String email,String nickName,String password,String emailCode) {
	    // 检查邮箱是否已注册
	    UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
	    if(null != userInfo){
	        throw new BusinessException("邮箱已经存在");
	    }
	    // 检查昵称是否已注册
	    UserInfo nickNameUser = this.userInfoMapper.selectByNickName(nickName);
	    if(null != nickNameUser){
	        throw new BusinessException("昵称已经存在");
	    }
	    // 验证邮箱验证码
	    emailCodeService.checkCode(email,emailCode);
	    // 生成用户ID
	    String userId = StringTools.getRandomNumber(Constants.LENGTH_10);
	    // 初始化用户信息对象
	    userInfo = new UserInfo();
	    userInfo.setUserId(userId);
	    userInfo.setNickName(nickName);
	    userInfo.setEmail(email);
	    // 对密码进行MD5加密
	    userInfo.setPassword(StringTools.encodeByMD5(password));
	    userInfo.setJoinTime(new Date());
	    userInfo.setStatus(UserStatusEnum.ENABLE.getStatus());
	    userInfo.setUseSpace(0L);
	    // 获取系统设置信息
	    SysSettingsDto sysSettingsDto = redisComponent.getSysSettingsDto();
	    // 设置用户总空间
	    userInfo.setTotalSpace(sysSettingsDto.getUserInitUseSpace() * Constants.MB);
	    // 插入用户信息到数据库
	    this.userInfoMapper.insert(userInfo);
	}
	/**
	 * 登录方法
	 * @param email 用户邮箱
	 * @param password 用户密码
	 * @return SessionWebUserDto类型的对象，包含用户会话信息
	 * 该方法通过用户邮箱查询用户信息，并验证密码是否正确
	 * 如果用户不存在或密码错误，抛出"账号或密码错误"的业务异常
	 * 如果用户账号被禁用，抛出"账号已被禁用"的业务异常
	 * 更新用户的最后登录时间，并根据用户状态设置sessionWebUserDto的admin属性
	 * 计算用户空间信息，并保存到Redis中
	 */
	@Override
	public SessionWebUserDto login(String email, String password) {
	    UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
	    if(null ==userInfo||!userInfo.getPassword().equals(password)){
	        throw new BusinessException("账号或密码错误");
	    }
	    if(UserStatusEnum.DISABLE.getStatus().equals(userInfo.getStatus())) {
	        throw new BusinessException("账号已被禁用");
	    }
	    UserInfo updateUserInfo = new UserInfo();
	    updateUserInfo.setLastLoginTime(new Date());
	    this.userInfoMapper.updateByEmail(updateUserInfo,email);
	    SessionWebUserDto sessionWebUserDto = new SessionWebUserDto();
	    sessionWebUserDto.setNickName(userInfo.getNickName());
	    sessionWebUserDto.setUserId(userInfo.getUserId());
	    if(ArrayUtils.contains(appConfig.getAdminEmails().split(","),email)){
	       sessionWebUserDto.setAdmin(true);
	    }else {
	        sessionWebUserDto.setAdmin(false);
	    }
		//用户空间
		UserSpaceDto userSpaceDto = new UserSpaceDto();
		userSpaceDto.setUseSpace(fileInfoService.getUserUseSpace(userInfo.getUserId()));
		userSpaceDto.setTotalSpace(userInfo.getTotalSpace());
		redisComponent.saveUserSpaceUse(userInfo.getUserId(), userSpaceDto);
		return sessionWebUserDto;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void resetPwd(String email, String password, String emailCode) {
	   UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
	   if(null ==userInfo){
	       throw new BusinessException("邮箱账号不存在");
	   }
	   emailCodeService.checkCode(email,emailCode);
	   UserInfo updateInfo = new UserInfo();
	   updateInfo.setPassword(StringTools.encodeByMD5(password));
	   this.userInfoMapper.updateByEmail(updateInfo,email);
	}

	@Override
	public SessionWebUserDto qqLogin(String code) {
		String accessToken = getQQAccessToken(code);
		String openId = getQQOpenId(accessToken);
		UserInfo user = this.userInfoMapper.selectByQqOpenId(openId);
		String avatar = null;
		if (null == user) {
			QQInfoDto qqInfo = getQQUserInfo(accessToken, openId);
			user = new UserInfo();

			String nickName = qqInfo.getNickname();
			nickName = nickName.length() > Constants.LENGTH_150 ? nickName.substring(0, 150) : nickName;
			avatar = StringTools.isEmpty(qqInfo.getFigureurl_qq_2()) ? qqInfo.getFigureurl_qq_1() : qqInfo.getFigureurl_qq_2();
			Date curDate = new Date();


			user.setQqOpenId(openId);
			user.setJoinTime(curDate);
			user.setNickName(nickName);
			user.setQqAvatar(avatar);
			user.setUserId(StringTools.getRandomString(Constants.LENGTH_10));
			user.setLastLoginTime(curDate);
			user.setStatus(UserStatusEnum.ENABLE.getStatus());
			user.setUseSpace(0L);
			user.setTotalSpace(redisComponent.getSysSettingsDto().getUserInitUseSpace() * Constants.MB);
			this.userInfoMapper.insert(user);
			user = userInfoMapper.selectByQqOpenId(openId);
		} else {
			UserInfo updateInfo = new UserInfo();
			updateInfo.setLastLoginTime(new Date());
			avatar = user.getQqAvatar();
			this.userInfoMapper.updateByQqOpenId(updateInfo, openId);
		}
		if (UserStatusEnum.DISABLE.getStatus().equals(user.getStatus())) {
			throw new BusinessException("账号被禁用无法登录");
		}
		SessionWebUserDto sessionWebUserDto = new SessionWebUserDto();
		sessionWebUserDto.setUserId(user.getUserId());
		sessionWebUserDto.setNickName(user.getNickName());
		sessionWebUserDto.setAvatar(avatar);
		if (ArrayUtils.contains(appConfig.getAdminEmails().split(","), user.getEmail() == null ? "" : user.getEmail())) {
			sessionWebUserDto.setAdmin(true);
		} else {
			sessionWebUserDto.setAdmin(false);
		}
		UserSpaceDto userSpaceDto = new UserSpaceDto();
		Long useSpace = this.fileInfoService.getUserUseSpace(user.getUserId());
		userSpaceDto.setUseSpace(useSpace);
		userSpaceDto.setTotalSpace(user.getTotalSpace());
		redisComponent.saveUserSpaceUse(user.getUserId(), userSpaceDto);
		return sessionWebUserDto;
	}
	private String getQQAccessToken(String code) {
		/**
		 * 返回结果是字符串 access_token=*&expires_in=7776000&refresh_token=* 返回错误 callback({UcWebConstants.VIEW_OBJ_RESULT_KEY:111,error_description:"error msg"})
		 */
		String accessToken = null;
		String url = null;
		try {
			url = String.format(appConfig.getQqUrlAccessToken(), appConfig.getQqAppId(), appConfig.getQqAppKey(), code, URLEncoder.encode(appConfig
					.getQqUrlRedirect(), "utf-8"));
		} catch (UnsupportedEncodingException e) {
			logger.error("encode失败");
		}
		String tokenResult = OKHttpUtils.getRequest(url);
		if (tokenResult == null || tokenResult.indexOf(Constants.VIEW_OBJ_RESULT_KEY) != -1) {
			logger.error("获取qqToken失败:{}", tokenResult);
			throw new BusinessException("获取qqToken失败");
		}
		String[] params = tokenResult.split("&");
		if (params != null && params.length > 0) {
			for (String p : params) {
				if (p.indexOf("access_token") != -1) {
					accessToken = p.split("=")[1];
					break;
				}
			}
		}
		return accessToken;
	}
	private String getQQOpenId(String accessToken) throws BusinessException {
		// 获取openId
		String url = String.format(appConfig.getQqUrlOpenId(), accessToken);
		String openIDResult = OKHttpUtils.getRequest(url);
		String tmpJson = this.getQQResp(openIDResult);
		if (tmpJson == null) {
			logger.error("调qq接口获取openID失败:tmpJson{}", tmpJson);
			throw new BusinessException("调qq接口获取openID失败");
		}
		Map jsonData = JsonUtils.convertJson2Obj(tmpJson, Map.class);
		if (jsonData == null || jsonData.containsKey(Constants.VIEW_OBJ_RESULT_KEY)) {
			logger.error("调qq接口获取openID失败:{}", jsonData);
			throw new BusinessException("调qq接口获取openID失败");
		}
		return String.valueOf(jsonData.get("openid"));
	}


	private QQInfoDto getQQUserInfo(String accessToken, String qqOpenId) throws BusinessException {
		String url = String.format(appConfig.getQqUrlUserInfo(), accessToken, appConfig.getQqAppId(), qqOpenId);
		String response = OKHttpUtils.getRequest(url);
		if (StringUtils.isNotBlank(response)) {
			QQInfoDto qqInfo = JsonUtils.convertJson2Obj(response, QQInfoDto.class);
			if (qqInfo.getRet() != 0) {
				logger.error("qqInfo:{}", response);
				throw new BusinessException("调qq接口获取用户信息异常");
			}
			return qqInfo;
		}
		throw new BusinessException("调qq接口获取用户信息异常");
	}

	private String getQQResp(String result) {
		if (StringUtils.isNotBlank(result)) {
			int pos = result.indexOf("callback");
			if (pos != -1) {
				int start = result.indexOf("(");
				int end = result.lastIndexOf(")");
				String jsonStr = result.substring(start + 1, end - 1);
				return jsonStr;
			}
		}
		return null;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
    public void updateUserStatus(String userId,Integer status){
         // 创建用户信息对象
         UserInfo userInfo = new UserInfo();
         // 设置用户状态
         userInfo.setStatus(status);
         // 如果用户状态为禁用
         if (UserStatusEnum.DISABLE.getStatus().equals(status)) {
             // 将用户可用空间设为0
             userInfo.setUseSpace(0L);
             // 删除用户所有文件
             fileInfoMapper.deleteFileByUserId(userId);
         }
         // 更新用户信息
         userInfoMapper.updateByUserId(userInfo, userId);
	}

	/**
	 * 根据用户ID更改用户的空间配额
	 *
	 * @param userId 用户ID，用于标识特定的用户
	 * @param changeSpace 用户空间配额需要变更的量，单位为兆字节（MB）
	 */
	@Override
	public  void changeUserSpace(String userId,Integer changeSpace){
	    // 将变更的空间量转换为字节
	    Long space = changeSpace * Constants.MB;

	    // 调用userInfoMapper中的方法来更新用户的空间配额
	    this.userInfoMapper.updateUserSpace(userId,null,space);

	    // 调用redisComponent中的方法重置用户的已使用空间缓存
	    this.redisComponent.resetUserSpaceUse(userId);
	}
}